from symbide import ModuleManager, SubprocessExecutionManager, OPTIONS, SHORTCUTS

import gtk
from widgetOrganizer import WidgetOrganizer
from errorWidget import Error
from sheet import Sheet
from symbide.src.execution.executor_pythonsheet import Executor 

from xml.dom import minidom

#if gtksourceview is not available, a gtk textview is used instead
__HAS_SOURCEVIEW__ = ModuleManager.hasModule('gtksourceview')

if __HAS_SOURCEVIEW__:
    import gtksourceview
    __TEXTVIEW__ = gtksourceview.SourceView
else:
    __TEXTVIEW__ = gtk.TextView


class Pythonsheet(Sheet, gtk.Frame):
    '''
    A widget used to write, edit and execute default python scripts without
    any subinputs or preparsing.
    '''
    
    def __init__(self, ancestor, text='', path=''):
        gtk.Frame.__init__(self)
        Sheet.__init__(self, 'Pythonsheet', ancestor, path)
        
        self.xmlTag = 'worksheet'
        self.pane = gtk.VPaned()
        self.pane.show()
        self.add(self.pane)
        
        self._createChildren(text)
        self.setChanged(False)
    
    def _createChildren(self, text):
        input = Input(self, text)
        self.addChild(input)
        self.pane.add1(input)
        output = Output(self)
        self.addChild(output)
        self.pane.add2(output)
        
        
    def set_onlyInput(self):
        if self.pane.get_parent() is self and self.get_child() is self.pane:
            gtk.Frame.remove(self, self.pane)
            self.getChildren('Input').reparent(self)
        
    def set_both(self):
        if not self.pane.get_parent() and self.get_child() is self.getChildren('Input'):
            gtk.Frame.remove(self, self.getChildren('Input'))
            self.add(self.pane)
            self.pane.add1(self.getChildren('Input'))
        
class Input(WidgetOrganizer, gtk.ScrolledWindow):
    '''
    Just a gtk.ScrolledWindow with a Textview.
    '''
    
    type = 'Pythonsheet'
    
    def __init__(self, ancestor, text=""):
        gtk.ScrolledWindow.__init__(self)
        WidgetOrganizer.__init__(self, 'Input', ancestor)
        self.xml = 'input'
        self.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        self._createTextView(text)
        
    def _createTextView(self, text=""):
        '''
        Creates a Textview and places it into the ScrolledWindow.
        '''
        
        if __HAS_SOURCEVIEW__:
            self.buffer = gtksourceview.SourceBuffer()
        else:
            self.buffer = gtk.TextBuffer()
        
        self.view = __TEXTVIEW__(self.buffer)
        
        self.add(self.view)
        tag = self.buffer.create_tag('global')
        
        self.buffer.set_text(text)
        self.view.show()

        
        #tag.set_property('size-points',17)
        tag.set_property('font', 'monospace regular 10')
        size = tag.get_property('size')
        self._updateTags()
        
        #event-handling
        self.view.connect('key_press_event', self._event_keyPress)
        self.view.connect('focus-in-event', self._event_focusIn)
        self.buffer.connect('changed', self._event_changed)
        self.view.connect('populate-popup', self._event_popup)
        self.buffer.connect('changed', self._event_changed)
        
        #sets the style of the textview
        self.view.set_border_window_size(gtk.TEXT_WINDOW_RIGHT, 1)
        self.view.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 1)
        self.view.set_border_window_size(gtk.TEXT_WINDOW_TOP, 1)
        self.view.set_border_window_size(gtk.TEXT_WINDOW_BOTTOM, 1)
        
        #TODO: let the styles be set by the user
            
        if __HAS_SOURCEVIEW__:
            manager = gtksourceview.SourceLanguagesManager()
            language = manager.get_language_from_mime_type("text/x-python")
                        
            self.buffer.set_language(language)
            self.buffer.set_highlight(True)
            
            #self.view.set_show_line_numbers(True)
            self.view.set_smart_home_end(True)
            #self.view.set_show_margin(True)
            self.view.set_tabs_width(int(OPTIONS['tabswidth']))
            self.view.set_auto_indent(True)
    
    def _event_focusIn(self, widget, *args):
        self.getAncestor('Notebook').activeInput = self
        self.getAncestor('Notebook').set_cursor_moved()
        
    def _event_changed(self, buffer):
        '''
        Update the styling tags in buffer, and set self.changed True.
        '''
        
        self._updateTags()
        self.changed = True
    
    def _event_popup(self, widget, popup):        
        menuitem = gtk.MenuItem('Open Pythonsheet as Worksheet')
        
        menuitem.connect('activate', lambda t:self.open_as_worksheet())
        menuitem.show()
        popup.prepend(menuitem)
        
    def _event_keyPress(self, widget, event):
        '''
        Reacts to shortcuts for evaluateCell, undo, redo.
        '''
        
        key = event.keyval
        masks = gtk.accelerator_get_default_mod_mask()
        
        events = (('executeCell', 'execute'),
                  ('executeWorksheet', 'execute'),
                  ('undo', 'undo'),
                  ('redo', 'redo'),
                  )
                  
        for i, j in events:
            keyval, modifier = SHORTCUTS[i]
            if key==keyval and masks & event.state==modifier:
                getattr(self, j)()
                return True
            
        events = (('openWorksheet', 'openWorksheet'),
                  ('saveWorksheet', 'saveWorksheet'),
                  ('saveWorksheetAs', 'saveWorksheetAs'))
        
        for i, j in events:
            keyval, modifier = SHORTCUTS[i]
            if key==keyval and masks & event.state==modifier:
                getattr(self.getAncestor('Notebook'), j)()
                return True
            
        keyval, modifier = SHORTCUTS['find']
        if key==keyval and masks & event.state==modifier:
            find = self.getAncestor('MainWindow').getChildren('Find')
            if self.buffer.get_has_selection():
                start, end = self.buffer.get_selection_bounds()
                text = self.buffer.get_text(start, end, include_hidden_chars=False)
                find.entry.set_text(text.split('\n', 2)[0])
            find.entry.grab_focus()
            find.show()
            
    def _event_moveCursor(self, textview, step_size, count, extend_selection):
        '''
        Ensures that the cursor is always visible.
        '''
        
        self.getAncestor('Notebook').cursor_moved = True
            
    def _updateTags(self):
        '''
        Update the styling tags in buffer.
        '''
        
        start, end = self.buffer.get_bounds()
        self.buffer.apply_tag_by_name('global', start, end)
        
    def undo(self):
        '''
        Undos the last user action in buffer. Only possible if
        gtksourceview is available.
        '''
        
        if __HAS_SOURCEVIEW__ and self.buffer.can_undo():
            self.buffer.undo()
        
    def redo(self):
        '''
        Redos the last user action in buffer. Only possible if
        gtksourceview is available.
        '''
        
        if __HAS_SOURCEVIEW__ and self.buffer.can_redo():
            self.buffer.redo()
    
    def placeCursor(self, neighbor=None, linePos=0,  focus=True):
        if isinstance(neighbor, gtk.TextIter):
            iter = neighbor
        else:
            iter = self.buffer.get_start_iter()
        iter.forward_lines(linePos)
        self.buffer.place_cursor(iter)
        if focus:
            self.view.grab_focus()
        self.view.scroll_to_mark(self.buffer.get_insert(), 0.4)
        
    def get_search_area(self, start_iter, forward):
        if forward:
            end_iter = self.buffer.get_end_iter()
            next_start_iter = self.buffer.get_start_iter()
        else:
            end_iter = self.buffer.get_start_iter()
            next_start_iter = self.buffer.get_end_iter()
        next_input = self
        return (start_iter, end_iter), (next_input, next_start_iter)
    
    def open_as_worksheet(self):
        w = self.getAncestor('Notebook').addWorksheet()
        w.getChildren('Input').buffer.set_text(self.get_text())
    
    def get_text(self):
        start, end = self.buffer.get_bounds()
        return self.buffer.get_text(start, end)
            
    def execute(self):
        '''
        Executes the text in the Input buffer in the execution thread.
        '''
        
        SubprocessExecutionManager.add_job(Executor(), self.type, self)
        
class Output(WidgetOrganizer, gtk.ScrolledWindow): 
    '''
    Just a gtk.ScrolledWindow with a Textview.
    '''
    
    def __init__(self, ancestor, text=""):
        gtk.ScrolledWindow.__init__(self)
        WidgetOrganizer.__init__(self, 'Output', ancestor)
        self.xml = 'output'
        self.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        self._createTextView(text)
        
    def _createTextView(self, text=""):
        self.buffer = gtk.TextBuffer()
        self.view = gtk.TextView(self.buffer)
        self.buffer.set_text(text)
        self.view.show()
        self.add(self.view)
        
        tag = self.buffer.create_tag('global')
        tag.set_property('size-points',10)
        self.updateTags()
        
        self.buffer.connect('changed', self._event_changed)
        
        self.view.set_editable(False)
        
        if not text:
            self.ancestor.set_onlyInput()
            return
        
    def _event_changed(self, buffer):
        '''
        Update the styling tags in buffer, and set changed True.
        '''
        
        self.changed = True
        self.updateTags()
        
    def updateTags(self):
        '''
        Update the styling tags in buffer.
        '''
        
        start, end = self.buffer.get_bounds()
        self.buffer.apply_tag_by_name('global', start, end)
        
    def set_output(self, out, err):
        '''
        Displays the output of the script.
        '''
        
        self.buffer.set_text(out)
        if err:
            self.addError(err)
        self.ancestor.set_both()
        
    def addError(self, error):
        error = minidom.parseString(error)
        
        #some shortcuts
        end = self.buffer.get_end_iter
        append = lambda text:self.buffer.insert(end(), text)
        error = Error(self.ancestor.getChildren('Input'), error)
        anchor = self.buffer.create_child_anchor(end())
        self.view.add_child_at_anchor(error, anchor)